using System;
using System.Collections.Generic;
using System.Text;

using SlimDX;
using SlimDX.Direct3D9;
using SlimDX.Windows;

namespace miciv.Rendering
{
    public class CameraBase : ICamera
    {
        protected CoreContext m_coreContext;
        protected Device m_d3dDevice;
        protected Matrix m_matView;
        protected Matrix m_matViewInv;
        protected Matrix m_matProj;
        protected Matrix m_matViewProj;
        protected Matrix m_matPosition;
        protected Matrix m_matRotation;
        protected Vector3 m_f3Position;
        protected Vector3 m_f3Rotation;

        protected float m_fUnprojectMinZ;
        public float UnprojectMinZ
        {
            get
            {
                return this.m_fUnprojectMinZ;
            }
            set
            {
                this.m_fUnprojectMinZ = value;
            }
        }

        protected float m_fUnprojectMaxZ;
        public float UnprojectMaxZ
        {
            get
            {
                return this.m_fUnprojectMaxZ;
            }
            set
            {
                this.m_fUnprojectMaxZ = value;
            }
        }

        public CameraBase(CoreContext _coreContext)
        {
            this.m_coreContext = _coreContext;
            this.m_d3dDevice = this.m_coreContext.GetData<Device>("D3DDevice");
            this.m_matView = new Matrix();
            this.m_matViewInv = new Matrix();
            this.m_matProj = new Matrix();
            this.m_matViewProj = new Matrix();
            this.m_matPosition = new Matrix(); ;
            this.m_matRotation = new Matrix(); ;
            this.m_f3Position = new Vector3();
            this.m_f3Rotation = new Vector3();
            this.m_fUnprojectMinZ = -1000.0f;
            this.m_fUnprojectMaxZ = 1000.0f;
        }

        protected void UpdateView()
        {
            //this.m_matView = this.m_matPosition * this.m_matRotation;
            Matrix.Multiply(ref this.m_matRotation, ref this.m_matPosition, out this.m_matView);
            Matrix.Invert(ref this.m_matView, out this.m_matViewInv);
            this.m_matViewProj = this.m_matViewInv * this.m_matProj;
        }

        #region ICamera Membres

        public void SetOrtho(float _fWidth, float _fHeight, float _fZNear, float _fZFar)
        {
            Matrix.OrthoLH(_fWidth, _fHeight, _fZNear, _fZFar, out this.m_matProj);
            UpdateView();
        }

        public void SetPerspective(float _fFovy, float _fAspectRatio, float _fZNear, float _fZFar)
        {
            float fToRadian = 3.14f / 180.0f;
            Matrix.PerspectiveFovLH(_fFovy * fToRadian, _fAspectRatio, _fZNear, _fZFar, out this.m_matProj);
            UpdateView();
        }

        public SlimDX.Vector3 Position
        {
            get
            {
                return this.m_f3Position;
            }
            set
            {
                this.m_f3Position = value;
                Matrix.Translation(ref this.m_f3Position, out this.m_matPosition);
                UpdateView();
            }
        }

        public Vector3 Rotation
        {
            get
            {
                return this.m_f3Rotation;
            }
            set
            {
                this.m_f3Rotation = value;
                float fToRadian = 3.14f / 180.0f;
                Matrix.RotationYawPitchRoll(
                    this.m_f3Rotation.Y * fToRadian,
                    this.m_f3Rotation.X * fToRadian,
                    this.m_f3Rotation.Z * fToRadian,
                    out this.m_matRotation);
                UpdateView();
            }
        }

        public Matrix ComputeWorldViewProj(ref SlimDX.Matrix _matWorld, bool _bTranspose)
        {
            if (false != _bTranspose)
            {
                return Matrix.Transpose(_matWorld * this.m_matViewProj);
            }
            return _matWorld * this.m_matViewProj;
        }

        public void ComputeWorldViewProj(ref SlimDX.Matrix _matWorld, out Matrix _matWVP, bool _bTranspose)
        {
            Matrix.Multiply(ref _matWorld, ref this.m_matViewProj, out _matWVP);
            if (false != _bTranspose)
            {
                Matrix.Transpose(ref _matWVP, out _matWVP);
            }
        }

        public Vector3 Unproject(float _fScreenX, float _fScreenY)
        {
            Vector3 f3Screen = new Vector3(_fScreenX, _fScreenY, 0.0f);
            Viewport viewport = this.m_d3dDevice.Viewport;
            return Vector3.Unproject(f3Screen, viewport.X, viewport.Y, viewport.Width, viewport.Height, this.m_fUnprojectMinZ, this.m_fUnprojectMaxZ, this.m_matViewProj);
        }

        #endregion
    }
}
